#include <utillib/memory/linearbuffer.h>
#include <utillib/memory/memory.h>
#include <string.h>

using namespace utillib::memory;

LinearBuffer::LinearBuffer(size_t size, bool resizable) : m_buffer( NULL ), m_usingBufferSize(0), m_bufferCapacity(0), m_beginOffset(0), m_resizable( resizable )
{
	if (0 < size)
		initialize(size);
}

LinearBuffer::~LinearBuffer()
{
	release();
}


bool LinearBuffer::resize( size_t size )
{
	if ( m_resizable == false ) return ( false );
	unsigned char *newBuffer = new byte[ size ];
	if(NULL != m_buffer)
	{
		memcpy( newBuffer, m_buffer + m_beginOffset, m_usingBufferSize );
		SAFE_DELETE_ARRAY( m_buffer );
	}
	m_buffer = newBuffer;
	m_bufferCapacity = size;
	m_beginOffset = 0;

	return ( true );
}

void LinearBuffer::realignBuffer()
{
	if ( m_usingBufferSize > 0 )
		memmove( m_buffer, m_buffer + m_beginOffset, m_usingBufferSize );
	m_beginOffset = 0;
}

bool LinearBuffer::resize()
{
	return ( resize( m_bufferCapacity * 2 ) );
}

unsigned char *LinearBuffer::endOfBuffer()
{
	return ( m_buffer + m_beginOffset + m_usingBufferSize );
}

unsigned char *LinearBuffer::beginOfBuffer()
{
	return ( m_buffer + m_beginOffset );
}

void LinearBuffer::resetOffset()
{
	m_beginOffset = 0;
	m_usingBufferSize = 0;
}

size_t LinearBuffer::offset( size_t offset )
{
	m_beginOffset += offset;
	m_usingBufferSize -= offset;

	return ( m_beginOffset );
}

size_t LinearBuffer::offset()
{
	return ( m_beginOffset );
}

void LinearBuffer::resetBufferSize()
{
	m_usingBufferSize = 0;
}

size_t LinearBuffer::bufferSize( size_t size )
{
	m_usingBufferSize += size;

	return ( m_usingBufferSize );
}

size_t LinearBuffer::bufferSize()
{
	return ( m_usingBufferSize );
}

size_t LinearBuffer::remainSize()
{
	return ( m_bufferCapacity - m_beginOffset - m_usingBufferSize );
}

size_t LinearBuffer::arrangeOffset( size_t size )
{
	if (m_usingBufferSize <= size)
	{
		resetOffset();
	}
	else
	{
		offset(size);
	}

	return ( m_usingBufferSize );
}

bool LinearBuffer::reserveBuffer( size_t size )
{
	if (remainSize() <= size)
	{
		if (m_beginOffset > size)
			realignBuffer();
		else
			return ( resize() );
	}
	return ( true );
}

unsigned char *LinearBuffer::read( unsigned char *ptr, size_t size )
{
	unsigned char *buffer = beginOfBuffer();
	if (m_usingBufferSize < size)
	{
		return buffer;
	}
	memcpy(ptr, buffer, size);
	
	m_usingBufferSize -= size;
	m_beginOffset += size;

	return ( buffer );
}

unsigned char *LinearBuffer::write( const unsigned char *ptr, size_t size )
{
	if ( reserveBuffer(size) == false )
		return NULL;
	if (remainSize() <= size)
		return NULL;
	unsigned char *buffer = endOfBuffer();
	memcpy(buffer, ptr, size);
	
	m_usingBufferSize += size;

	return ( buffer );
}

void LinearBuffer::reset()
{
	resetOffset();
	resetBufferSize();
}

void LinearBuffer::initialize( size_t size )
{
	SAFE_DELETE_ARRAY( m_buffer );
	m_buffer = new byte[ size ];
	m_bufferCapacity = size;
	m_beginOffset = 0;
	m_usingBufferSize = 0;
}

void LinearBuffer::release()
{
	SAFE_DELETE_ARRAY( m_buffer );
	m_beginOffset = 0;
}

size_t LinearBuffer::capacity()
{
	return ( m_bufferCapacity );
}